using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Collections;

namespace dender.dvdcover.core
{
    /// <summary>
    /// CoverBase defines the abstraction of an entity containing a width, 
    /// heigh , parts and lines
    /// </summary>
    public abstract class CoverBase<T> : DimensionBase  where T : Part
    {
        /// <summary>
        /// Gets of sets the cutting lines of a cover
        /// </summary>
        /// <exception cref="System.ArgumentNullException">
        /// Throws an ArgumentNullException if the collection is null
        /// </exception>
        private List<CuttingLine> mCuttingLines;
        public List<CuttingLine> CuttingLines
        {
            get
            {
                return mCuttingLines;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("Lines");
                mCuttingLines = value;
            }
        }

        /// <summary>
        /// Gets or Sets the part of the cover
        /// </summary>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// Throws an ArgumentOutOfRangeException when a part is not located in a cover 
        ///</exception>
        private Dictionary<string,T> mPart;
        public Dictionary<string, T> Parts
        {
            get
            {
                Dictionary<string, T> returnList = new Dictionary<string, T>(mPart);
                return returnList;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("Parts");

                if (value.Count > 0)
                {
                    mPart.Clear();
                    foreach (KeyValuePair<string, T> p in value)
                    {
                        if (!CheckPartInCoverBase(p.Value))
                        {
                            throw new ArgumentOutOfRangeException("Part");
                        }
                        mPart.Add(p.Key, (T)p.Value);

                    }
                }
            }
        }

        public CoverBase(DimensionBase toCopy)
            : base(toCopy)  
        {
            InitializeLists();
        }

        public CoverBase(float width, float height)
            : base(width, height)
        {
            InitializeLists();
        }

        private void InitializeLists()
        {
            mCuttingLines = new List<CuttingLine>();
            mPart = new Dictionary<string, T>();
        }


        /// <summary>
        /// Checks if a part is in the borders of a cover
        /// </summary>
        /// <param name="p">Part to check</param>
        /// <returns>True if the part is in the border of the cover</returns>
        public bool CheckPartInCoverBase(T p)
        {
            return
                (
                    (p.Location.X >= 0 && p.Location.X + p.Width <= Width) &&
                    (p.Location.Y >= 0 && p.Location.Y + p.Height <= Height)
                );
        }

        /// <summary>
        /// Adds a part to the Parts collection, use this method instead of the Parts.Add() method
        /// The Parts propery returns a copy of the Parts list, using the add method on that will
        /// add the part to the copy of the Part list
        /// This construction if for protecting the two-way reference
        /// </summary>
        /// <param name="part"></param>
        public void AddPart(T part)
        {
            if (part != null && !Parts.ContainsKey(part.Name))
            {
                if (this.CheckPartInCoverBase(part))
                {
                    mPart.Add(part.Name,part);
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Part");
                }
            }
            else
            {
                throw new ArgumentNullException("Part");
            }

        }

        /// <summary>
        /// Deletes a part from the colection, use this method instead of the Parts.Remove() method
        /// The Parts property return a copy of the Parts list, using the Remove() mathod on that will
        /// remove the part from the copy of the part list.
        /// This construction if for protecting the two-way reference
        /// </summary>
        /// <param name="part">Part to Delete</param>
        /// <returns>True if the part is deleted, false if the part is not deleted or not found in 
        /// the collection</returns>
        public bool DeletePart(T part)
        {
            return mPart.Remove(part.Name);
        }
    }
}
